#include <Windows.h>
#include <tchar.h>
#include <assert.h>
#include "Resource.h"

#define		WM_CAPTURESRCN	(WM_APP + 101)
#define		WM_CAPTURECTRL	(WM_APP + 102)

LRESULT CALLBACK _DisplayWndProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam);
HWND _CreateDisplayWnd(HWND hParent);

LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{	
	static const UINT IDC_BTNUNDATEBK	= 1001;
	static const UINT IDC_BTNCAPTURESCRN= 1002;
	static const UINT IDC_BTNCAPTURECTRL= 1003;
	static const UINT IDC_BTNEXIT		= 1004;

	static HWND hWndDisplay;

	static HBITMAP hBkgnd1;
	static HBITMAP hBkgnd2;
	static HBITMAP hBkgnd; 
	switch (nMsg)
	{
		case WM_CREATE:
		{
			//--: Move Main wnd to center of screen
			RECT rtMainWnd;
			GetClientRect(hWnd, &rtMainWnd);
			UINT nSrnWidth		= GetSystemMetrics(SM_CXFULLSCREEN);
			UINT nSrnHeight		= GetSystemMetrics(SM_CYFULLSCREEN);
			UINT nWndWidth		= (rtMainWnd.right - rtMainWnd.left);
			UINT nWndHeight		= (rtMainWnd.bottom - rtMainWnd.top);
			UINT nLeft			= (nSrnWidth - nWndWidth) / 2;
			UINT nTop			= (nSrnHeight - nWndHeight) / 2;
			MoveWindow(hWnd, nLeft, nTop, nWndWidth, nWndHeight, TRUE);

			CreateWindow(_T("Button"), _T("Update Background"), WS_VISIBLE|WS_CHILD, 0, 0, 150, 50, hWnd, (HMENU)IDC_BTNUNDATEBK, NULL, NULL);
			CreateWindow(_T("Button"), _T("Capture Screen"), WS_VISIBLE|WS_CHILD, 160, 0, 150, 50, hWnd, (HMENU)IDC_BTNCAPTURESCRN, NULL, NULL);
			CreateWindow(_T("Button"), _T("Capture Control"), WS_VISIBLE|WS_CHILD, 320, 0, 150, 50, hWnd, (HMENU)IDC_BTNCAPTURECTRL, NULL, NULL);
			CreateWindow(_T("Button"), _T("Exit"), WS_VISIBLE|WS_CHILD, 480, 0, 100, 50, hWnd, (HMENU)IDC_BTNEXIT, NULL, NULL);
						
			hBkgnd1 = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP1));
			hBkgnd2 = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP2));
		
			hWndDisplay = _CreateDisplayWnd(hWnd);
			break;
		}
		case WM_COMMAND:
		{
			switch (wParam)
			{
				case IDC_BTNUNDATEBK:
				{
					static int nIndex = 0;
					switch (nIndex)
					{
						case 0:		hBkgnd = hBkgnd1;	nIndex++;		break;
						case 1:		hBkgnd = hBkgnd2;	nIndex++;		break;
						case 2:		hBkgnd = NULL;		nIndex = 0;		break;
					}
					BITMAP bmp;
					GetObject(hBkgnd, sizeof(BITMAP), &bmp);
					SetWindowPos(hWnd, NULL, 0, 0, bmp.bmWidth, bmp.bmHeight, SWP_NOMOVE|SWP_NOZORDER);
					SetWindowPos(hWndDisplay, NULL, 0, 0, bmp.bmWidth, bmp.bmHeight, SWP_NOMOVE|SWP_NOZORDER);
					InvalidateRect(hWnd, NULL, TRUE);
					break;
				}
				case IDC_BTNCAPTURESCRN:
				{
					if (NULL != hWndDisplay)
					{
						SendMessage(hWndDisplay, WM_CAPTURESRCN, 0, 0);
					}
					break;
				}
				case IDC_BTNCAPTURECTRL:
				{
					if (NULL != hWndDisplay)
					{
						SendMessage(hWndDisplay, WM_CAPTURECTRL, 0, 0);
					}
					break;
				}
				case IDC_BTNEXIT:
				{
					PostQuitMessage(0);
					break;
				}
			}
			break;
		}
		case WM_PAINT:
		{
			PAINTSTRUCT pt;
			HDC hDC = BeginPaint(hWnd, &pt);
			
			if (hBkgnd)
			{
				RECT rtWnd;
				GetClientRect(hWnd, &rtWnd);
				int nWidth		= rtWnd.right - rtWnd.left;
				int nHeight		= rtWnd.bottom - rtWnd.top;
				HDC hMemDC		= CreateCompatibleDC(hDC);
				HBITMAP hBitmap = CreateCompatibleBitmap(hDC, nWidth, nHeight);
				HGDIOBJ hObj	= SelectObject(hMemDC, hBitmap);
				HDC hMemDC2		= CreateCompatibleDC(hDC);		
				HGDIOBJ hObj2	= SelectObject(hMemDC, hBkgnd);
				BitBlt(hMemDC, 0, 0, nWidth, nHeight, hMemDC2, 0, 0, SRCCOPY);
				BitBlt(hDC, 0, 0, nWidth, nHeight, hMemDC, 0, 0, SRCCOPY);
				SelectObject(hMemDC, hObj);
				DeleteObject(hMemDC);
			}

			EndPaint(hWnd, &pt);
			return 0;
		}
		case WM_LBUTTONDOWN:
		{
			PostMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION, lParam);
			break;
		}
		case WM_DESTROY:
		{
			PostQuitMessage(0);
			break;
		}
	}

	return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
}

//////////////////////////////////////////////////////////////////////////
bool RegisterWndClass(const TCHAR* szClsName, WNDPROC fnWndProc)
{
	WNDCLASSEX wcs		= {0};
	wcs.cbSize			= sizeof(WNDCLASSEX);
	wcs.hbrBackground	= (HBRUSH)GetStockObject(COLOR_APPWORKSPACE);
	wcs.lpszClassName	= szClsName;
	wcs.lpfnWndProc		= fnWndProc;
	wcs.hCursor			= LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
	wcs.hIcon			= LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_APPLICATION));
	wcs.style			= CS_VREDRAW|CS_HREDRAW;
	
	return (0 != RegisterClassEx(&wcs));
}

//////////////////////////////////////////////////////////////////////////
HWND CreateWnd(const TCHAR* szClsName, const TCHAR* szWndCaption, HWND hParent = NULL)
{
	if (NULL != szClsName)
	{
		DWORD dwStyleEx = WS_EX_LEFT|WS_EX_LTRREADING;
		DWORD dwStyle   = WS_POPUP|WS_VISIBLE|WS_CLIPSIBLINGS|WS_SYSMENU;
		HWND hWnd = CreateWindowEx(dwStyleEx, szClsName, szWndCaption, dwStyle, 0, 0, 800, 480, hParent, NULL, GetModuleHandle(NULL), NULL);
	
		if (NULL != hWnd && IsWindow(hWnd))
		{
			ShowWindow(hWnd, SW_SHOW);
			UpdateWindow(hWnd);
			return hWnd;
		}
	}
	return NULL;
}

//////////////////////////////////////////////////////////////////////////
void RunMessageLoop()
{
	MSG Msg;
	while (true)
	{
		while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
		{
			if (WM_QUIT == Msg.message)
			{
				return;
			}

			TranslateMessage(&Msg);
			DispatchMessage(&Msg);
		}
	}
}
//////////////////////////////////////////////////////////////////////////
BOOL WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPTSTR szCmdLine, int nShowCmd)
{
	const TCHAR* szClsName		= _T("Win32App");
	const TCHAR* szWndCaption	= _T("Demo0048");

	if (RegisterWndClass(szClsName, WndProc))
	{
		if (NULL != CreateWnd(szClsName, szWndCaption))
		{
			RunMessageLoop();
		}
	}

	return TRUE;
}


//////////////////////////////////////////////////////////////////////////
HBITMAP _CaptureWindow(HWND hWnd, bool bCaptureCtrl)
{	
	RECT rtWnd;
	GetWindowRect(hWnd, &rtWnd);

	HDC hDC = GetDC(hWnd);
	HDC hDCMem = CreateCompatibleDC(hDC);
	HBITMAP hBitmap = CreateCompatibleBitmap(hDC, rtWnd.right - rtWnd.left, rtWnd.bottom - rtWnd.top);
	
	HGDIOBJ hOld = SelectObject(hDCMem, hBitmap);

	if (bCaptureCtrl)
	{
		SendMessage(hWnd, WM_PRINT, (WPARAM)hDCMem, PRF_CHILDREN|PRF_CLIENT|PRF_ERASEBKGND|PRF_NONCLIENT|PRF_OWNED);
	} else {
		BitBlt(hDCMem, 0, 0, rtWnd.right - rtWnd.left, rtWnd.bottom - rtWnd.top, hDC, 0, 0, SRCCOPY);
	}
	SelectObject(hDCMem, hOld);
	DeleteObject(hDCMem);
	ReleaseDC(hWnd, hDC);

	return hBitmap;
}

LRESULT CALLBACK _DisplayWndProc(HWND hWnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP hBitmap = NULL;
	switch (nMessage)
	{
		case WM_CAPTURESRCN:
		{
			HWND hWndParent = GetParent(hWnd);

			hBitmap = _CaptureWindow(hWndParent, false);

			::InvalidateRect(hWnd, NULL, TRUE);

			break;
		}
		case WM_CAPTURECTRL:
		{
			HWND hWndParent = GetParent(hWnd);

			hBitmap = _CaptureWindow(hWndParent, true);

			::InvalidateRect(hWnd, NULL, TRUE);

			break;
		}
		case WM_PAINT:
		{
			PAINTSTRUCT pt;
			HDC hDC = BeginPaint(hWnd, &pt);

			if (NULL != hBitmap)
			{
				HDC hMemDC = CreateCompatibleDC(hDC);
				HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hBitmap);
				BITMAP bmp;
				GetObject(hBitmap, sizeof(BITMAP), &bmp);
				BitBlt(hDC, 0, 0, bmp.bmWidth, bmp.bmHeight, hMemDC, 0, 0, SRCCOPY);
				SelectObject(hMemDC, hOldBmp);
				DeleteObject(hMemDC);
			}

			EndPaint(hWnd, &pt);
			return 0;
		}
	}

	return DefWindowProc(hWnd, nMessage, wParam, lParam);
}

HWND hWnd = NULL;
HWND _CreateDisplayWnd(HWND hParent)
{
	const TCHAR* szClsName = _T("DisplayWnd");
	if (RegisterWndClass(szClsName, _DisplayWndProc))
	{
		SetWindowLongPtr(hWnd, GWL_EXSTYLE, GetWindowLongPtr(hWnd, GWL_EXSTYLE) | WS_EX_STATICEDGE);
		hWnd = CreateWnd(szClsName, NULL, hParent);
	}
	
	return hWnd;
}
